﻿using System;
using UnityEngine;

public class Tensor
{
	public IntPtr Ptr;
	private bool Deleted;

	public Tensor(int rows, int cols)
	{
		Ptr = Eigen.Create(rows, cols);
		Deleted = false;
	}

	public void Delete()
	{
		if (!Deleted)
		{
			Eigen.Delete(Ptr);
			Deleted = true;
		}
	}

	// 行数
	public int GetRows()
	{
		return Eigen.GetRows(Ptr);
	}

	// 列数
	public int GetCols()
	{
		return Eigen.GetCols(Ptr);
	}

	// 元素全部置0
	public void SetZero()
	{
		Eigen.SetZero(Ptr);
	}

	// resize
	public void SetSize(int rows, int cols)
	{
		Eigen.SetSize(Ptr, rows, cols);
	}

	public void SetValue(int row, int col, float value)
	{
		if (row >= GetRows() || col >= GetCols())
		{
			Debug.Log("Setting out of bounds at [" + row + ", " + col + "].");
			return;
		}
		Eigen.SetValue(Ptr, row, col, value);
	}

	public float GetValue(int row, int col)
	{
		if (row >= GetRows() || col >= GetCols())
		{
			Debug.Log("Getting out of bounds at [" + row + ", " + col + "].");
			return 0f;
		}
		return Eigen.GetValue(Ptr, row, col);
	}

	// 矩阵加
	public static Tensor Add(Tensor lhs, Tensor rhs, Tensor OUT)
	{
		if (lhs.GetRows() != rhs.GetRows() || lhs.GetCols() != rhs.GetCols())
		{
			Debug.Log("Incompatible tensor dimensions.");
		}
		else
		{
			Eigen.Add(lhs.Ptr, rhs.Ptr, OUT.Ptr);
		}
		return OUT;
	}

	// 矩阵减
	public static Tensor Subtract(Tensor lhs, Tensor rhs, Tensor OUT)
	{
		if (lhs.GetRows() != rhs.GetRows() || lhs.GetCols() != rhs.GetCols())
		{
			Debug.Log("Incompatible tensor dimensions.");
		}
		else
		{
			Eigen.Subtract(lhs.Ptr, rhs.Ptr, OUT.Ptr);
		}
		return OUT;
	}

	// 矩阵乘矩阵
	public static Tensor Product(Tensor lhs, Tensor rhs, Tensor OUT)
	{
		if (lhs.GetCols() != rhs.GetRows())
		{
			Debug.Log("Incompatible tensor dimensions.");
		}
		else
		{
			Eigen.Product(lhs.Ptr, rhs.Ptr, OUT.Ptr);
		}
		return OUT;
	}

	// 矩阵乘标量
	public static Tensor Scale(Tensor lhs, float value, Tensor OUT)
	{
		Eigen.Scale(lhs.Ptr, value, OUT.Ptr);
		return OUT;
	}

	//矩阵按元素乘
	public static Tensor PointwiseProduct(Tensor lhs, Tensor rhs, Tensor OUT)
	{
		if (lhs.GetRows() != rhs.GetRows() || lhs.GetCols() != rhs.GetCols())
		{
			Debug.Log("Incompatible tensor dimensions.");
		}
		else
		{
			Eigen.PointwiseProduct(lhs.Ptr, rhs.Ptr, OUT.Ptr);
		}
		return OUT;
	}

	// 矩阵按元素除
	public static Tensor PointwiseQuotient(Tensor lhs, Tensor rhs, Tensor OUT)
	{
		if (lhs.GetRows() != rhs.GetRows() || lhs.GetCols() != rhs.GetCols())
		{
			Debug.Log("Incompatible tensor dimensions.");
		}
		else
		{
			Eigen.PointwiseQuotient(lhs.Ptr, rhs.Ptr, OUT.Ptr);
		}
		return OUT;
	}

	// 矩阵按元素求绝对值
	public static Tensor PointwiseAbsolute(Tensor IN, Tensor OUT)
	{
		Eigen.PointwiseAbsolute(IN.Ptr, OUT.Ptr);
		return OUT;
	}

	public static Tensor PCA(Tensor T, int dim)
	{
		Eigen.PCA(T.Ptr, dim);
		return T;
	}

	public static Tensor PCA_With_Matrix(Tensor X, int dim, Tensor V)
	{
		Eigen.PCA_With_Matrix(X.Ptr, dim, V.Ptr);
		return X;
	}

	// 行平均
	public float RowMean(int row)
	{
		if (row >= GetRows())
		{
			Debug.Log("Accessing out of bounds.");
			return 0f;
		}
		return Eigen.RowMean(Ptr, row);
	}

	// 列平均
	public float ColMean(int col)
	{
		if (col >= GetCols())
		{
			Debug.Log("Accessing out of bounds.");
			return 0f;
		}
		return Eigen.ColMean(Ptr, col);
	}

	// 行标准差
	public float RowStd(int row)
	{
		if (row >= GetRows())
		{
			Debug.Log("Accessing out of bounds.");
			return 0f;
		}
		return Eigen.RowStd(Ptr, row);
	}

	// 列标准差
	public float ColStd(int col)
	{
		if (col >= GetCols())
		{
			Debug.Log("Accessing out of bounds.");
			return 0f;
		}
		return Eigen.ColStd(Ptr, col);
	}

	// 按行求和
	public float RowSum(int row)
	{
		if (row >= GetRows())
		{
			Debug.Log("Accessing out of bounds.");
			return 0f;
		}
		return Eigen.RowSum(Ptr, row);
	}

	// 按列求和
	public float ColSum(int col)
	{
		if (col >= GetCols())
		{
			Debug.Log("Accessing out of bounds.");
			return 0f;
		}
		return Eigen.ColSum(Ptr, col);
	}

	public void Print()
	{
		string output = string.Empty;
		for (int i = 0; i < GetRows(); i++)
		{
			for (int j = 0; j < GetCols(); j++)
			{
				output += GetValue(i, j) + " ";
			}
			output += "\n";
		}
		Debug.Log(output);
	}

	public static Tensor operator +(Tensor b, Tensor c)
	{
		if (b.GetCols() != c.GetCols() || b.GetRows() != c.GetRows()) return null;
		Tensor Out = new Tensor(b.GetRows(), b.GetRows());
		return Add(b, c, Out);
	}

	public static Tensor operator -(Tensor b, Tensor c)
	{
		if (b.GetCols() != c.GetCols() || b.GetRows() != c.GetRows()) return null;
		Tensor Out = new Tensor(b.GetRows(), b.GetRows());
		return Subtract(b, c, Out);
	}

	public static Tensor operator *(float v, Tensor t)
	{
		Tensor Out = new Tensor(t.GetRows(), t.GetCols());
		return Scale(t, v, Out);
	}

	public static Tensor operator *(Tensor t, float v)
	{
		Tensor Out = new Tensor(t.GetRows(), t.GetCols());
		return Scale(t, v, Out);
	}
}
